<template>
  <div class="home">
    <div class="view-container" ref="threeDBox"></div>
    <div class="tooltip-box" :style="tooltipPosition" ref="tooltipBox">
      <div class="container">
        <div class="title">标题：{{ tooltopContent.title }}</div>
        <div class="explain">说明：{{ tooltopContent.text }}</div>
      </div>
    </div>
    <p class="title-text" ref="titleBox" :style="titlePosition">
      {{ tooltopContent.title }}
    </p>
  </div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import gsap from "gsap";
export default {
  name: "Home",
  data() {
    return {
      scene: null, //场景
      camera: null, //摄像机
      controls: null, //相机控制
      renderer: null, //渲染器
      axisHelper: null, //三维坐标系
      sphere: null, //球
      time: {
        value: 0,
      },
      //图片和信息点数据
      dataList: [
        {
          image: require("@/assets/image/livingRoom.jpg"),
          tipsList: [
            {
              position: { x: -200, y: -4, z: -147 },
              content: {
                title: "进入厨房",
                text: "",
                image: 1,
                showTip: false,
                showTitle: true,
              },
            },
            {
              position: { x: -100, y: 0, z: -231 },
              content: {
                title: "信息点2",
                text: "77989",
                showTip: true,
                showTitle: false,
              },
            },
            {
              position: { x: 150, y: -50, z: -198 },
              content: {
                title: "信息点3",
                text: "qwdcz",
                showTip: true,
                showTitle: false,
              },
            },
            {
              position: { x: 210, y: 11, z: -140 },
              content: {
                title: "信息点4",
                text: "大豆食心虫侦察十大大苏打大大大大大大大",
                showTip: true,
                showTitle: false,
              },
            },
            {
              position: { x: 208, y: -12, z: 140 },
              content: {
                title: "信息点5",
                text: "eq",
                showTip: true,
                showTitle: false,
              },
            },
            // {
            //   position: { x: 86, y: -9, z: 236 },
            //   content: {
            //     title: "进入房间",
            //     text: "",
            //     showTip: true,
            //     showTitle: false,
            //   },
            // },
          ],
        },
        {
          image: require("@/assets/image/kitchen.jpg"),
          tipsList: [
            {
              position: { x: -199, y: -24, z: 145 },
              content: {
                title: "进入大厅",
                text: "",
                image: 0,
                showTip: false,
                showTitle: true,
              },
            },
          ],
        },
      ],
      tipsSpriteList: [],
      tooltipPosition: {
        top: "-100%",
        left: "-100%",
      },
      titlePosition: {
        top: "-100%",
        left: "-100%",
      },
      tooltopContent: {},
    };
  },
  methods: {
    //初始化场景对象
    initScene() {
      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color(0x101010);
    },
    //初始化相机对象-透视投影相机
    initCamera(element) {
      this.camera = new THREE.PerspectiveCamera(
        45,
        //客户区大小,content+padding
        element.clientWidth / element.clientHeight,
        0.1,
        1000
      );
      this.camera.position.set(50, 0, 40);
    },
    //初始化轨道控制器,使得相机围绕目标进行轨道运动
    initControls(element) {
      //左键旋转，右键平移(禁)，滚动缩放
      this.controls = new OrbitControls(this.camera, element);
      this.controls.minDistance = 1;
      this.controls.maxDistance = 100;
      //禁止右键平移，会跑出去
      this.controls.enablePan = false;
      // this.controls.autoRotate = true; //自动旋转
    },
    //初始化渲染器
    initRenderer(element) {
      this.renderer = new THREE.WebGLRenderer();
      //偏移量content+padding+border
      this.renderer.setSize(element.offsetWidth, element.offsetHeight);
      //追加canvas元素到canvas3d元素中
      element.appendChild(this.renderer.domElement);
    },
    //初始化内容
    initContent(index = 0) {
      //球体几何对象
      let sphereGeometry = new THREE.SphereGeometry(16, 50, 50);
      sphereGeometry.scale(16, 16, -16);
      //纹理对象
      let texture = new THREE.TextureLoader().load(this.dataList[index].image);
      //基础网格材质对象，不受带有方向光源影响，没有棱角感
      let sphereMaterial = new THREE.MeshBasicMaterial({ map: texture });
      this.sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
      this.scene.add(this.sphere);
      this.addTipsSprite();
    },
    //加入精灵模型对象(与网格模型Mesh一样都是模型对象，需要材质不需要几何体)
    addTipsSprite(index = 0) {
      let tipTexture = new THREE.TextureLoader().load(
        require("@/assets/image/tip.png")
      );
      let material = new THREE.SpriteMaterial({ map: tipTexture });
      this.tipsSpriteList = [];
      this.dataList[index].tipsList.forEach((item) => {
        let sprite = new THREE.Sprite(material);
        sprite.scale.set(10, 10, 10);
        sprite.position.set(item.position.x, item.position.y, item.position.z);
        sprite.content = item.content;
        this.tipsSpriteList.push(sprite);
        this.scene.add(sprite);
      });
    },
    //场景切换
    changeContentAndtips(index) {
      //清除场景数据内所有的精灵标签
      this.scene.children = this.scene.children.filter(
        (item) => String(item.type) !== "Sprite"
      );
      //储存数组置空
      this.tipsSpriteList = [];
      //重新加载贴图，这边应用gasp做一个简单的过渡动画，将透明度opacity从0完全透明 ~ 1完全不透明
      let texture = new THREE.TextureLoader().load(this.dataList[index].image);
      let sphereMaterial = new THREE.MeshBasicMaterial({
        map: texture,
        //如果材质的transparent属性未设置为true，则材质将保持完全不透明
        transparent: true,
        opacity: 0,
      });
      this.sphere.material = sphereMaterial;
      //transparent设置为true时，通过设置材质的opacity属性来控制材质透明的程度。
      gsap.to(sphereMaterial, { transparent: true, opacity: 1, duration: 2 });
      //手动更新投影矩阵
      this.camera.updateProjectionMatrix();
      //添加当前场景标签
      this.addTipsSprite(index);
    },
    //渲染
    render() {
      //更新控制器。在摄像机的变换发生任何手动改变后调用，或如果.autoRotate或.enableDamping被设置时，在update循环里调用
      this.controls.update();
      this.renderer.render(this.scene, this.camera);
      //定义渲染器是否需要对对象排序,针对透明度
      this.renderer.sortObjects = false;
      //浏览器在下次重绘之前调用指定的回调函数更新动画
      this.timer = requestAnimationFrame(this.render);
      this.time.value += 0.015;
    },
    //相机自适应渲染
    onResize() {
      let element = this.$refs.threeDBox;
      this.camera.aspect = element.clientWidth / element.clientHeight;
      //更新相机的投影矩阵
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(element.clientWidth, element.clientHeight);
    },
    //鼠标点击，切换视角
    onMouseClick(e) {
      e.preventDefault();
      let element = this.$refs.threeDBox;
      let raycaster = new THREE.Raycaster();
      //保存鼠标坐标信息
      let mouse = new THREE.Vector2();
      mouse.x = (e.clientX / element.clientWidth) * 2 - 1;
      mouse.y = -(e.clientY / element.clientHeight) * 2 + 1;
      //更新鼠标和射线位置
      raycaster.setFromCamera(mouse, this.camera);
      //获取和射线相交的物体
      let intersects = raycaster.intersectObjects(this.tipsSpriteList, true);
      //切换视角并隐藏原来的tip
      if (intersects.length > 0 && intersects[0].object.content.showTitle) {
        this.changeContentAndtips(intersects[0].object.content.image);
        this.handleTooltipHide(e);
      }
      /*
      //获取鼠标点击的三维坐标
      var raycaster = new THREE.Raycaster();//光线投射，用于确定鼠标点击位置
      var mouse = new THREE.Vector2();//创建二维平面
      //将html坐标系转化为webgl坐标系，并确定鼠标点击位置
      mouse.x = (e.clientX / this.$refs.threeDBox.clientWidth) * 2 - 1;
      mouse.y = -((e.clientY / this.$refs.threeDBox.clientHeight) * 2) + 1;
      raycaster.setFromCamera(mouse, this.camera);//以camera为z坐标，确定所点击物体的3D空间位置
      var intersects = raycaster.intersectObjects(this.scene.children);
      if (intersects.length) {
        var selected = intersects[0]; //取第一个物体
        console.log("x坐标:" + selected.point.x);
        console.log("y坐标:" + selected.point.y);
        console.log("z坐标:" + selected.point.z);
      }
      */
    },
    //鼠标移入，内容弹出
    onMousemove(e) {
      e.preventDefault();
      let element = this.$refs.threeDBox;
      let raycaster = new THREE.Raycaster();
      let mouse = new THREE.Vector2();
      //通过鼠标点击的位置计算出raycaster所需要的点的位置，以屏幕中心为原点，值的范围为-1到1.
      mouse.x = (e.clientX / element.clientWidth) * 2 - 1;
      mouse.y = -(e.clientY / element.clientHeight) * 2 + 1;
      raycaster.setFromCamera(mouse, this.camera);
      //将标签精灵数据放进来做视线交互
      let intersects = raycaster.intersectObjects(this.tipsSpriteList, true);
      //视线穿过集合选择最前面的一个
      if (intersects.length > 0) {
        //将标签的空间坐标转屏幕坐标，通过计算赋给元素的top、left
        let elementWidth = element.clientWidth / 2;
        let elementHeight = element.clientHeight / 2;
        let worldVector = new THREE.Vector3(
          intersects[0].object.position.x,
          intersects[0].object.position.y,
          intersects[0].object.position.z
        );
        let position = worldVector.project(this.camera);
        this.tooltopContent = intersects[0].object.content;
        if (intersects[0].object.content.showTip) {
          let left = Math.round(
            elementWidth * position.x +
              elementWidth -
              this.$refs.tooltipBox.clientWidth / 2
          );
          let top = Math.round(
            -elementHeight * position.y +
              elementHeight -
              this.$refs.tooltipBox.clientHeight / 2
          );
          this.tooltipPosition = {
            left: `${left}px`,
            top: `${top}px`,
          };
        } else if (intersects[0].object.content.showTitle) {
          let left = Math.round(
            elementWidth * position.x +
              elementWidth -
              this.$refs.titleBox.clientWidth / 2
          );
          let top = Math.round(-elementHeight * position.y + elementHeight);
          this.titlePosition = {
            left: `${left}px`,
            top: `${top}px`,
          };
        }
      } else {
        //鼠标移出去隐藏所有
        this.handleTooltipHide(e);
      }
    },
    handleTooltipHide(e) {
      e.preventDefault();
      this.tooltipPosition = {
        top: "-100%",
        left: "-100%",
      };
      this.titlePosition = {
        top: "-100%",
        left: "-100%",
      };
      this.tooltopContent = {};
    },
  },
  mounted() {
    let element = this.$refs.threeDBox;
    this.initScene();
    this.initCamera(element);
    this.initControls(element);
    this.initContent();
    this.initRenderer(element);
    this.render();
    window.addEventListener("resize", this.onResize, false);
    window.addEventListener("click", this.onMouseClick, false);
    this.renderer.domElement.addEventListener(
      "mousemove",
      this.onMousemove,
      false
    );
    this.$refs.tooltipBox.addEventListener(
      "mouseleave",
      this.handleTooltipHide,
      false
    );
  },
  destroyed() {
    cancelAnimationFrame(this.timer);
  },
};
</script>

<style lang="scss" scoped>
.home {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden; //隐藏窗口区域滚动条
  .view-container {
    width: 100%;
    height: 100%;
    overflow: hidden;
  }
  .title-text {
    position: absolute;
    width: 66px;
    color: #382129;
  }
  .tooltip-box {
    position: absolute;
    padding: 0px 0px 40px 0px;
    line-height: 30px;
    border-radius: 4px;
    color: #ffffff;
    z-index: 100;
    cursor: pointer;
    .container {
      position: relative;
      width: 240px;
      max-height: 200px;
      padding: 10px;
      background-color: rgba(0, 0, 0, 0.6);
      // &::before {
      //   content: "";
      //   position: absolute;
      //   bottom: -16px;
      //   left: 20%;
      //   border-top: 16px solid rgba(0, 0, 0, 0.8);
      //   border-left: 10px solid transparent;
      //   border-right: 10px solid transparent;
      // }
      .title {
        width: 100%;
        padding: 6px 0;
      }
      .explain {
        width: 100%;
        max-height: 100px;
        font-size: 14px;
        overflow-y: auto;
        &::-webkit-scrollbar {
          width: 4px;
          height: 4px;
        }
        &::-webkit-scrollbar-track {
          background: #353535;
          border-radius: 2px;
        }
        &::-webkit-scrollbar-thumb {
          background: #cdcdcd;
          border-radius: 2px;
        }
        &::-webkit-scrollbar-thumb:hover {
          background: #9c9c9c;
        }
        &::-webkit-scrollbar-corner {
          background: #f6f6f6;
        }
      }
    }
  }
}
</style>
